package com.xneure.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import com.xneure.utils.bean.ClassInfo;
import com.xneure.utils.bean.GUID;
import com.xneure.utils.exception.ParseException;
import com.xneure.utils.intf.DefaultFieldProvide;
import com.xneure.utils.intf.FieldHandler;
import com.xneure.utils.intf.GenericsFilter;
import com.xneure.utils.intf.InitializingField;
import com.xneure.utils.intf.ObjectConvertHandle;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.LazyLoader;
import net.sf.cglib.proxy.MethodInterceptor;

/**
 * <p>
 * 对象操作工具类
 * </p>
 * <p>
 * Copyright: 版权所有<br>
 *
 * @author zhangjun
 * @version 2015年9月6日
 */
public class ObjectUtil{
	/**
	 * 对象转Map 值=null 自动 过滤
	 * 
	 * @param obj
	 * @return Map<String,Object>
	 */
	public static Map<String,Object> ObjectToMap(Object obj){
		return ObjectToMap(obj,new DefaultFieldProvide());
	}

	/**
	 * 判断是否为基本数据类型
	 * 
	 * @param obj object or class
	 * @return boolean
	 */
	public static boolean isBasicType(Object obj){
		try{
			Class<?> cls = obj instanceof Class ? (Class<?>)obj : obj.getClass();
			return cls.equals(Object.class) || getObjectClass(cls).isPrimitive();
		}catch(Exception e){
			return false;
		}
	}

	public static Class<?> getObjectClass(Object obj){
		Class<?> cls = obj instanceof Class ? (Class<?>)obj : obj.getClass();
		try{
			if(cls.isPrimitive() == false){
				Field declaredField = cls.getDeclaredField("TYPE");
				if(null == declaredField){
					return cls;
				}
				return (Class<?>)declaredField.get(null);
			}else{
				return cls;
			}
		}catch(Exception e){
			return cls;
		}
	}

	public static boolean isSubclass(Object obj,Class<?>... cls){
		Class<?> cs = obj instanceof Class ? (Class<?>)obj : obj.getClass();
		for(Class<?> _c:cls){
			if(_c.isAssignableFrom(cs)){
				return true;
			}
		}
		return false;
	}

	public static Class<?> getTargetType(Field field){
		Type genericType = field.getGenericType();
		if(genericType instanceof ParameterizedType){
			genericType = ((ParameterizedType)genericType).getActualTypeArguments()[0];
		}
		return (Class<?>)genericType;
	}

	@SuppressWarnings("unchecked")
	public static <T> T objectProxy(Class<T> cls,MethodInterceptor mi,Object... param){
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(cls);
		enhancer.setCallback(mi);
		if(ObjectUtil.isNull(param)){
			return (T)enhancer.create();
		}else{
			Class<?>[] types = new Class[param.length];
			for(int i = 0;i < param.length;i++){
				types[i] = param[i].getClass();
			}
			return (T)enhancer.create(types,param);
		}
	}

	@SuppressWarnings("unchecked")
	public static <T> T fieldProxy(Class<T> cls,LazyLoader ll){
		return (T)Enhancer.create(cls,ll);
	}

	public static Map<String,Object> ObjectToMap(Object obj,FieldHandler fh){
		if(null == obj){
			return null;
		}
		ClassInfo rm = new ClassInfo(obj);
		Map<String,Object> map = rm.getFieldValues();
		Map<String,Object> rMap = new HashMap<String,Object>(map.size());
		for(Entry<String,Object> ent:map.entrySet()){
			Object value = ent.getValue();
			String key = ent.getKey();
			// 深度转换
			if(null != value){
				if(value instanceof List || value.getClass().isArray()){
					value = collectionToArray(fh,value);
				}else if(value instanceof Map){
					value = mapConvert(fh,value);
				}else if(value instanceof Number || value instanceof Date || value instanceof java.sql.Date){
					value = value.toString();
				}else if(String.class != value.getClass()){
					value = ObjectToMap(value,fh);
				}
			}
			if(fh.filter(key,value)){
				rMap.put(key,fh.getValue(key,value));
			}
		}
		return rMap;
	}

	/**
	 * map 数据类型的转换
	 * 
	 * @param fh
	 * @param value
	 * @return Map<String,Object>
	 */
	public static Map<String,Object> mapConvert(FieldHandler fh,Object value){
		Map<?,?> objMap = (Map<?,?>)value;
		Map<String,Object> lhm = new LinkedHashMap<String,Object>(objMap.size());
		for(Map.Entry<?,?> tmpEnt:objMap.entrySet()){
			Object tmpKey = tmpEnt.getKey();
			Object tmpVal = tmpEnt.getValue();
			if(fh.filter(tmpKey.toString(),tmpVal)){
				lhm.put(tmpKey.toString(),tmpVal);
			}
		}
		return lhm;
	}

	/**
	 * 数组类型的数据转换
	 * 
	 * @param fh
	 * @param value 数组或Collection 包含java对象
	 * @return Object[]
	 */
	public static Object[] collectionToArray(FieldHandler fh,Object value){
		if(value.getClass().isArray()){
			value = Arrays.asList((Object[])value);
		}
		Collection<?> list = (Collection<?>)value;
		Object[] tmpObj = new Object[list.size()];
		int i = 0;
		for(Object o:list){
			tmpObj[i++] = ObjectToMap(o,fh);
		}
		return tmpObj;
	}

	@SuppressWarnings("unchecked")
	public static <T> T newInstance(String className){
		try{
			return (T)newInstance(Class.forName(className));
		}catch(ClassNotFoundException e){
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	public static <T> T newInstance(Class<T> c,Object... param){
		try{
			return (T)getConstructor(c,param).newInstance(param);
		}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}

	public static Constructor<?> getConstructor(Class<?> c,Object... param){
		int length = null == param ? 0 : param.length;
		Class<?>[] types = new Class[length];
		for(int i = 0;i < length;i++){
			if(param[i] instanceof Class){
				types[i] = (Class<?>)param[i];
			}else{
				types[i] = param[i].getClass();
			}
		}
		while(c.equals(Object.class) == false){
			Constructor<?>[] dc = c.getDeclaredConstructors();
			for(Constructor<?> _c:dc){
				Type[] pt = _c.getGenericParameterTypes();
				if(pt.length == length){
					boolean skip = false;
					for(int i = 0;i < length;i++){
						if(pt[i].getClass().isAssignableFrom(types[i]) == false){
							skip = true;
							break;
						}
					}
					if(skip){
						skip = false;
					}else{
						return _c;
					}
				}
			}
			c = c.getSuperclass();
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	public static <T> T[] newArrays(Class<T> c,int length){
		try{
			if(c.isArray()){
				return (T[])Array.newInstance(c.getComponentType(),length);
			}else{
				return (T[])Array.newInstance(c,length);
			}
		}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}

	public static boolean containNull(Object... objs){
		if(isNotNull(objs)){
			for(Object obj:objs){
				if(isNull(obj)){
					return true;
				}
			}
			return false;
		}
		return true;
	}

	public static boolean isContainNull(Object... objs){
		if(isNull(objs)){
			return true;
		}
		for(Object obj:objs){
			if(isNull(obj)){
				return true;
			}
		}
		return false;
	}

	@SuppressWarnings("rawtypes")
	public static boolean isNull(Object obj){
		if(null == obj){
			return true;
		}
		if(obj.getClass().equals(String.class)){
			return obj.toString().trim().isEmpty();
		}
		if(obj instanceof Map){
			return ((Map)obj).isEmpty();
		}else if(obj instanceof Collection){
			return ((Collection)obj).isEmpty();
		}else if(obj.getClass().isArray()){
			Object[] os = (Object[])obj;
			if(os.length < 1){
				return true;
			}else{
				for(Object o:os){
					if(isNotNull(o)){
						return false;
					}
				}
				return true;
			}
		}else if(null != obj.getClass()){
			return false;
		}
		return true;
	}

	public static boolean isNotNull(Object obj){
		return isNull(obj) == false;
	}

	public static <Obj> Obj mapToObject(Map<String,Object> map,ObjectConvertHandle handle){
		handle.start();
		for(Map.Entry<String,Object> entry:map.entrySet()){
			handle.progress(entry.getKey(),entry.getValue());
		}
		handle.end();
		return handle.getResult();
	}

	@SuppressWarnings("unchecked")
	public static <Obj> Obj mapToObject(Map<String,Object> map,Class<Obj> cls){
		if(Map.class.isAssignableFrom(cls)){
			return (Obj)map;
		}
		Obj o = newInstance(cls);
		ClassInfo rm = new ClassInfo(o);
		rm.setMethodValues(map);
		return o;
	}

	@SuppressWarnings("unchecked")
	public static <Obj> List<Obj> mapToObject(List<Map<String,Object>> list,ObjectConvertHandle handle){
		List<Object> _tmpList = new ArrayList<Object>(list.size());
		for(Map<String,Object> map:list){
			_tmpList.add(mapToObject(map,handle));
		}
		return (List<Obj>)_tmpList;
	}

	public static Class<?> getGenerics(GenericsFilter filter){
		Class<?> current = filter.searchClass();
		while(current != filter.superClass()){
			Type type = current.getGenericSuperclass();
			if(type instanceof ParameterizedType){
				Type[] arrayOfType;
				int j = (arrayOfType = ((ParameterizedType)type).getActualTypeArguments()).length;
				for(int i = 0;i < j;++i){
					Type t = arrayOfType[i];
					if((t instanceof Class) && (filter.targetClass().isAssignableFrom((Class<?>)t))){
						return ((Class<?>)t);
					}
				}
			}
			current = current.getSuperclass();
		}
		throw new RuntimeException(GenericsFilter.errMsg);
	}

	/**
	 * 拼接数组 = o1 + o2
	 */
	@SuppressWarnings("unchecked")
	public static <Obj> Obj[] arrayJoin(Obj[] o1,Obj... o2){
		if(isNull(o1) && isNull(o2)){
			return null;
		}
		if(isNull(o1) && isNull(o2) == false){
			return o2;
		}
		if(isNull(o2) && isNull(o1) == false){
			return o1;
		}
		Obj[] o = (Obj[])Array.newInstance(o1.getClass().getComponentType(),o1.length + o2.length);
		System.arraycopy(o1,0,o,0,o1.length);
		System.arraycopy(o2,0,o,o1.length,o2.length);
		return o;
	}

	public static Class<?> getGenericInterfaceFirst(Object obj){
		if(obj instanceof Class){
		}else{
			obj = obj.getClass();
		}
		Type[] current = ((Class<?>)obj).getGenericInterfaces();
		if(null != current && current.length > 0){
			return (Class<?>)((ParameterizedType)current[0]).getActualTypeArguments()[0];
		}
		return null;
	}

	public static Class<?> getGenericsFirst(Object obj){
		if(obj instanceof Class){
		}else{
			obj = obj.getClass();
		}
		Type[] current = ((Class<?>)obj).getGenericInterfaces();
		ParameterizedType type = (ParameterizedType)((Class<?>)obj).getGenericSuperclass();
		if(null != type){
			current = type.getActualTypeArguments();
			if(current[0] instanceof ParameterizedType){
				type = (ParameterizedType)current[0];
				return (Class<?>)type.getActualTypeArguments()[0];
			}
			return (Class<?>)current[0];
		}
		return null;
	}

	public static void modifyFieldValue(Object obj,String fieldName,Object val){
		try{
			if(obj == null || fieldName == null || fieldName.trim().length() < 1){
				return;
			}
			Class<? extends Object> class1 = peelingCGLIB(obj.getClass());
			Field field = null;
			while(class1.equals(Object.class) == false && null == field){
				for(Field f:class1.getDeclaredFields()){
					if(f.getName().equals(fieldName)){
						field = f;
						break;
					}
				}
				if(null == field){
					class1 = class1.getSuperclass();
				}
			};
			if(null == field){
				return;
			}
			modifyFieldValue(obj,field,val);
		}catch(Exception e){
		}
	}

	public static void modifyFieldValue(Object obj,Field field,Object val){
		try{
			if(obj == null || field == null){
				return;
			}
			field.setAccessible(true);
			if(Modifier.isFinal(field.getModifiers())){
				Field modifiersField = Field.class.getDeclaredField("modifiers");
				modifiersField.setAccessible(true);
				modifiersField.setInt(field,field.getModifiers() & ~Modifier.FINAL);
				field.set(obj,val);
				modifiersField.setInt(field,field.getModifiers() | Modifier.FINAL);
				modifiersField.setAccessible(false);
			}else{
				field.set(obj,val);
			}
			field.setAccessible(false);
		}catch(Exception e){
		}
	}

	public static Object callMethod(Object obj,String methodName,Object... val){
		try{
			if(obj == null || methodName == null || methodName.trim().length() < 1){
				return null;
			}
			Class<?>[] type = null;
			if(null != val){
				type = new Class<?>[val.length];
				for(int i = 0;i < val.length;i++){
					type[i] = val.getClass();
				}
			}
			Class<? extends Object> class1 = peelingCGLIB(obj.getClass());
			Method method = null;
			while(class1 != null){
				method = class1.getDeclaredMethod(methodName,type);
				if(null != method){
					break;
				}
				if(null == method){
					class1 = class1.getSuperclass();
				}
			};
			return callMethod(obj,method,val);
		}catch(Exception e){
			return null;
		}
	}

	public static Object callMethod(Object obj,Method method,Object... val){
		Object rs = null;
		try{
			if(obj == null || method == null){
				return null;
			}
			if(isNull(val)){
				val = null;
			}
			boolean var = method.isAccessible();
			method.setAccessible(true);
			rs = method.invoke(obj,val);
			method.setAccessible(var);
		}catch(Exception e){
			e.printStackTrace();
		}
		return rs;
	}

	@SuppressWarnings("unchecked")
	public static <T> T getFieldValue(Object obj,String... fieldName){
		Object tmp = obj;
		for(String str:fieldName){
			tmp = getFieldValue(tmp,str);
		}
		return (T)tmp;
	}

	public static <T> T getFieldValue(Object obj,String fieldName){
		try{
			if(obj == null || fieldName == null || fieldName.isEmpty()){
				return null;
			}
			Class<? extends Object> class1 = peelingCGLIB(obj.getClass());
			Field field = null;
			while(class1.equals(Object.class) == false && null == field){
				for(Field f:class1.getDeclaredFields()){
					if(f.getName().equals(fieldName)){
						field = f;
						break;
					}
				}
				if(null == field){
					class1 = class1.getSuperclass();
				}
			};
			if(null == field){
				return null;
			}
			return getFieldValue(obj,field);
		}catch(Exception e){
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	public static <T> T getFieldValue(Object obj,Field field){
		try{
			if(obj == null || field == null){
				return null;
			}
			field.setAccessible(true);
			Object val;
			if(Modifier.isFinal(field.getModifiers())){
				Field modifiersField = Field.class.getDeclaredField("modifiers");
				modifiersField.setAccessible(true);
				modifiersField.setInt(field,field.getModifiers() & ~Modifier.FINAL);
				val = field.get(obj);
				modifiersField.setInt(field,field.getModifiers() | Modifier.FINAL);
				modifiersField.setAccessible(false);
			}else{
				val = field.get(obj);
			}
			field.setAccessible(false);
			return (T)val;
		}catch(Exception e){
			return null;
		}
	}

	public static Class<?> peelingCGLIB(Class<?> cls){
		if(isCglib(cls)){
			cls = cls.getSuperclass();
		}
		return cls;
	}

	public static boolean isCglib(Class<?> cls){
		return cls.getSimpleName().contains("$$");
	}

	public static Field getField(Class<?> cls,String fieldName){
		if(null == cls){
			return null;
		}
		if(isGeneralType(cls)){
			throw new ParseException("不支持基本数据类型。");
		}
		if(null != cls){
			while(cls.equals(Object.class) == false){
				try{
					Field f = cls.getDeclaredField(fieldName);
					if(isNotNull(f)){
						return f;
					}
				}catch(Exception e){
				}
				cls = cls.getSuperclass();
			}
		}
		return null;
	}

	public static Method getMethod(Class<?> cls,String methodName,Class<?>... paramType){
		if(null == cls){
			return null;
		}
		if(isGeneralType(cls)){
			throw new ParseException("不支持基本数据类型。");
		}
		while(null != cls){
			try{
				Method[] method = cls.getDeclaredMethods();
				for(Method m:method){
					if(m.getName().equals(methodName)){
						if(null != paramType){
							if(m.getParameterCount() == 0){
								return m;
							}
							if(m.getParameterCount() == paramType.length){
								Class<?>[] parameterTypes = m.getParameterTypes();
								for(int i = 0;i < parameterTypes.length;i++){
									if(parameterTypes[i].isAssignableFrom(paramType[i]) == false){
										break;
									}
								}
								return m;
							}
						}
						return m;
					}
				}
			}catch(Exception e){
			}
			cls = cls.getSuperclass();
		}
		return null;
	}

	@SuppressWarnings({"unchecked","rawtypes"})
	public static Object convertValue(Object val,Class<?> cls){
		if(null == val){
			return val;
		}
		cls = getObjectClass(cls);
		String lowerCase = cls.getSimpleName().toLowerCase();
		if(cls.isAssignableFrom(ObjectUtil.getObjectClass(val))){
			return val;
		}
		if(lowerCase.equals("date")){
			Date strToDate = DateUtil.autoFormat(val.toString());
			if(java.sql.Date.class.equals(cls)){
				strToDate = new java.sql.Date(strToDate.getTime());
			}
			return strToDate;
		}else if(lowerCase.equals("bigdecimal")){
			return new BigDecimal(val.toString());
		}else if(lowerCase.equals("guid")){
			return GUID.valueOf(val.toString());
		}else if(lowerCase.equals("uuid")){
			return UUID.fromString(val.toString());
		}else if(lowerCase.equals("string")){
			return val.toString();
		}else if(lowerCase.equals("long")){
			return Long.valueOf(val.toString());
		}else if(lowerCase.equals("integer") || lowerCase.equals("int")){
			return Integer.valueOf(val.toString());
		}else if(lowerCase.equals("double")){
			return Double.valueOf(val.toString());
		}else if(lowerCase.equals("float")){
			return Float.valueOf(val.toString());
		}else if(lowerCase.equals("boolean")){
			if(Boolean.valueOf(val.toString()) == false){
				return "0".equals(val.toString()) == false;
			}
			return true;
		}else if(lowerCase.equals("byte")){
			return Byte.valueOf(val.toString());
		}else if(lowerCase.equals("character") || lowerCase.equals("char")){
			return (char)Integer.valueOf(val.toString()).intValue();
		}else if(lowerCase.equals("timestamp")){
			return (Timestamp)Timestamp.valueOf(val.toString());
		}else if(cls.isArray()){
			Object[] objs = (Object[])val;
			for(int i = 0;i < objs.length;i++){
				objs[i] = convertValue(objs[i],objs[i].getClass());
			}
			return objs;
		}else if(cls.isEnum()){
			return (Enum)Enum.valueOf((Class<Enum>)cls,val.toString());
		}else{
			return val;
		}
	}

	public static boolean isGeneralType(Class<?> cls){
		if(isBasicType(cls)){
			return true;
		}else if(cls.isArray()){
			return isGeneralType(cls.getComponentType());
		}else{
			return isSubclass(cls,CharSequence.class,Number.class,List.class,Map.class,Date.class,Set.class,java.sql.Date.class);
		}
	}

	public static <T> T autoInit(Class<T> cls,InitializingField fInit){
		return fieldProxy(cls,new LazyLoader(){
			@Override
			public Object loadObject() throws Exception{
				Object initField = fInit.initField();
				ObjectUtil.modifyFieldValue(fInit.getThis(),fInit.fieldName(),initField);
				return initField;
			}
		});
	}

	/**
	 * 返回修改之前的数据
	 */
	public static Object changeAnnotationValue(Annotation annotation,String key,Object newValue) throws RuntimeException{
		InvocationHandler handler = Proxy.getInvocationHandler(annotation);
		Map<String,Object> memberValues = getFieldValue(handler,"memberValues");
		if(memberValues.containsKey(key)){
			Object oldValue = memberValues.get(key);
			if(oldValue != null && oldValue.getClass() != newValue.getClass()){
				throw new IllegalArgumentException("数据类型不匹配：".concat(oldValue.getClass().getSimpleName()));
			}
			memberValues.put(key,newValue);
			return oldValue;
		}else{
			throw new IllegalArgumentException("不存在的属性：".concat(key));
		}
	}
}
